package dianmobile.eistarandroid.network.request;

import android.content.Context;
import android.util.Log;

import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.cookies.Cookie;
import com.android.volley.cookies.OnCookiesDataChangedListener;
import com.android.volley.toolbox.HttpHeaderParser;

import org.apache.http.impl.cookie.DateParseException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public abstract class ByhhAndroidRequest extends Request<Map<String, Object>> {

    private static final String LOG_TAG = "ByhhAndroidRequest";

    /** application context */
    private Context context;
    /** network request params */
    protected Map<String, String> params;
    /** network request callback */
    protected RequestCallback callback;

    protected static void executeSuper(Context context, RequestType type, Map<String, String> params, RequestCallback callback) {

        RequestQueue requestQueue = RequestFactory.getRequestQueue(context);
        ByhhAndroidRequest request = RequestFactory.buildRequest(type, params, callback);
        requestQueue.add(request);
    }

    public ByhhAndroidRequest(int method, String url, Map<String, String> params, final RequestCallback callback,
                              Response.ErrorListener listener) {
        super(method, url, listener);

        this.params = params;
        this.callback = callback;

    }

    public ByhhAndroidRequest(int method, String url, Response.ErrorListener listener) {
        super(method, url, listener);
    }

    /**
     * the volley request will call this method to get request params
     * @return request params
     */
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return this.params;
    }

    protected Context getContext() {
        return this.context;
    }

    /**
     * the form of the network response data is XML.
     * this method will the XML data, and put them into a result map.
     * <p/>
     * the subclass of {@link ByhhAndroidRequest} can implements the {@link #parseXmlToMap(XmlPullParser, Map)}
     * to define thw way changing XML data to map.
     * @param response
     * @return
     */
    @Override
    protected Response<Map<String, Object>> parseNetworkResponse(NetworkResponse response) {
        Map<String, Object> result = new HashMap<>();

        try {
            // create a xml parse from network response data.
//            String xmlString = new String(response.data,
//                    HttpHeaderParser.parseCharset(response.headers));
            String xmlString = new String(response.data, "gb2312");
            Log.i("ByhhAndroidRequest", xmlString);//TODO 删除
            //解析XML数据
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlString));

            // changing xml response data into a result map
            parseXmlToMap(xmlPullParser, result);
//            // changing html response data into a result map
//            Document doc = Jsoup.parse(xmlString);
//            parseHtmlToMap(doc, result);

                     if (result.containsKey(COOKIES)) {
                saveCookies((Map<String, String>) result.get("cookies"));
            }

            return Response.success(result, HttpHeaderParser.parseCacheHeaders(response));

        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (XmlPullParserException e) {
            return Response.error(new ParseError(e));
        }
    }

    /**
     * all request use the same onCookieDataChangedListener instance.
     */
    protected static OnCookiesDataChangedListener onCookiesDataChangedListener;

    /**
     * using this method to configure cookie data changed listener, and the response cookies
     * data will be saved.
     * @param listener
     */
    public static void setOnCookiesDataChangedListener(OnCookiesDataChangedListener listener) {
        if (listener == null)
            throw new NullPointerException();

        if (onCookiesDataChangedListener != null)
            throw new IllegalStateException("onCookiesDataChangedListener has already changed");

        onCookiesDataChangedListener = listener;
    }

    protected static final String COOKIES = "cookies";
    private static final String CONST_COOKIE_VALUE_DOMAIN = "dian.hust.edu.cn:81";
    private static final String CONST_COOKIE_VALUE_PATH = "/";

    protected void saveCookies(Map<String, String> cookiesMap) {
        // if did not configure cookies data changed listener, do nothing.
        if (onCookiesDataChangedListener == null)
            return ;

        List<Cookie> cookiesContainer = new ArrayList<>();

        /* every key-value in this map is the key-value on a cookie bean. */
        Set<String> cookies = cookiesMap.keySet();
        for (String key : cookies) {
            // create a cookie bean instance.
            Cookie cookieBean = new Cookie();
            cookieBean.setKey(key);
            cookieBean.setValue(cookiesMap.get(key));
            // set cookie's const value
            cookieBean.setDomain(CONST_COOKIE_VALUE_DOMAIN);
            cookieBean.setPath(CONST_COOKIE_VALUE_PATH);
            final Date simpleDate;
            try {
                simpleDate = org.apache.http.impl.cookie.DateUtils.parseDate("Mon, 17 Jul 2017 12:46:12 GMT");
                simpleDate.after(new Date());   // update the time.
                cookieBean.setExpires(simpleDate);
            } catch (DateParseException e) {
                e.printStackTrace();
                cookieBean.setExpires(null);    // means cookie has not time.
            }

            // add cookie bean to cookie container.
            cookiesContainer.add(cookieBean);
        }

        // using OnCookieDataChangedListener to save cookies.
        onCookiesDataChangedListener.saveCookies(cookiesContainer);
    }

    /**
     * delivering the network response data to the request callback.
     * <b this method is running in the UI thread. />
     * @param response
     */
    @Override
    protected void deliverResponse(Map<String, Object> response) {
        callback.onResult(response, null);
    }

    /**
     * Because different network request will cause different response data
     * that representing in different XML from.
     * <p/>
     * A subclass of {@link ByhhAndroidRequest} represents a network request, and each
     * request subclass should override this abstract method to define the way
     * parsing the network response xml data.
     * @param parser    parser from network response data
     * @param result   a map used to store response data
     */
    protected abstract void parseXmlToMap(XmlPullParser parser, Map<String, Object> result);

    //protected abstract void parseHtmlToMap(Document doc, Map<String, Object> result);
}
